 using System;
using System.IO;
using System.Collections.Generic;

namespace RFServer.Database
{
    class Storage
    {
        private const string DROPBOX = "dropbox:";
        private static DriverStorage local;         /* Локальное хранилиде %APPDATA%\RFServer\db.sdf    */ 
        private static bool useDropBox;             /* Задействовать ли выборку объектов из DrobBox     */

        static Storage()
        {
            local = new DriverStorage(Setup.DatabasePathFile);
            useDropBox = Setup.DropBoxInUse;

            if (useDropBox)
            {
                if (!Directory.Exists(Setup.DropBoxRFSDirectory))
                {
                    useDropBox = false;

                    Setup.DropBoxInUse = false;
                    Setup.Log("[storage]: DropBox is in use but directory '" + Setup.DropBoxRFSDirectory + 
                        "' does not exists. Try to restart server to create the directory");
                }
            }
        }

        private static string DropBoxGetRelativePath(string RealFilename)
        {
            // Путь к каталогу DropBox является относительным, так как
            // он разный на разных машинах. Путь путь к каталогу DrobBox
            // начинается с префикса DROPBOX:folder\file

            return RealFilename.Replace(Setup.DropBoxDirectory, DROPBOX);
        }

        private static string DropBoxGetRealPath(string RelativeFilename)
        {
            // Путь к каталогу DropBox является относительным, так как
            // он разный на разных машинах. Путь путь к каталогу DrobBox
            // начинается с префикса DROPBOX:folder\file

            // Функция преобразует относительный путь для DropBox в
            // реальное имя файла на текущей машине

            return RelativeFilename.Replace(DROPBOX, Setup.DropBoxDirectory);
        }

        private static string[] DropBoxGetRealPath(string[] RelativeFilenames)
        {
            if (RelativeFilenames == null)
                return null;

            for (int i = 0; i < RelativeFilenames.Length; i++)
                RelativeFilenames[i] = DropBoxGetRelativePath(RelativeFilenames[i]);

            return RelativeFilenames;
        }

        public static bool Insert(string[] keywords)
        {
            return Insert(keywords, true);
        }

        public static bool Insert(string[] keywords, bool toDropBox)
        {
            // Вставка категорий и связанных с ним объета
            // Если toDropBox == true, то по умолчанию делает попытка
            // добавить объект в базу данных DropBox, если он ей принадлежит

            try
            {
                // Определяем имя файла

                string filename = keywords[keywords.Length - 1];

                // Определяем базу данных, в которую его следует добавлять

                if (toDropBox)
                {
                    if (Setup.InDropBox(filename))
                    {
                        bool result = false;

                        using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                        {
                            // Путь к каталогу DropBox является относительным, так как
                            // он разный на разных машинах. Путь путь к каталогу DrobBox
                            // начинается с префикса DROPBOX:folder\file

                            keywords[keywords.Length - 1] = DropBoxGetRelativePath(filename);

                            // Добавляем в базу данных DropBox

                            result = dropbox.Insert(keywords);
                        }

                        return result;
                    }
                }

                return local.Insert(keywords);
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in Insert", e);
                return false;
            }
        }

        public static List<string> Select(string[] keywords)
        {
            try
            {
                List<string> select = local.Select(keywords);

                if (select == null)
                    return null;

                if (useDropBox)
                {
                    List<string> selectDropBox = null;

                    using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                    {
                        selectDropBox = dropbox.Select(keywords);
                    }

                    if (selectDropBox == null)
                        return null;

                    if (selectDropBox.Count == 0)
                        return select;

                    // Путь к каталогу DropBox является относительным, так как
                    // он разный на разных машинах. Путь к каталогу DrobBox
                    // начинается с префикса DROPBOX:folder\file

                    foreach (string item in selectDropBox)
                        select.Add(DropBoxGetRealPath(item));
                }

                return select;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in Select", e);
                return null;
            }
        }

        public static List<string> Tags(string[] keywords)
        {
            try
            {
                List<string> tags = local.Tags(keywords);

                if (tags == null)
                    return null;

                if (useDropBox)
                {
                    // В массиве keywords указаны файлы (как правило, один),
                    // тэги которого следует вернуть. Для каждого файла выполняем
                    // преобразование его путь в относительный путь для DropBox

                    keywords = DropBoxGetRealPath(keywords);

                    // Извлекаем тэги для файлов вида DROPBOX:folder\file

                    List<string> tagsDropBox = null;
                        
                    using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                    {
                        tagsDropBox = dropbox.Tags(keywords);
                    }

                    if (tagsDropBox == null)
                        return null;

                    foreach (string tag in tagsDropBox)
                        tags.Add(tag);
                }

                return tags;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in Tags", e);
                return null;
            }
        }

        public static bool TagExists(string tag)
        {
            try
            {
                if (local.TagExists(tag))
                    return true;

                if (useDropBox)
                {
                    using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                    {
                        return dropbox.TagExists(tag);
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in TagExists", e);
                return false;
            }
        }

        public static bool Rename(string[] keywords)
        {
            try
            {
                if (!local.Rename(keywords))
                    return false;

                if (useDropBox)
                {
                    // Если хотя бы один объект находится в DropBox, то выполняем
                    // операцию в базе данных DropBox

                    // Синтаксис:
                    // RENAME [OBJECT | CATEGORY] object1 object2

                    bool hasObjectsDrobBox = false;

                    try
                    {
                        for (int i = 2; i < keywords.Length; i++)
                            if (Setup.InDropBox(keywords[i]))
                            {
                                hasObjectsDrobBox = true;
                                break;
                            }
                    }
                    catch (Exception e)
                    {
                        Setup.Log("[storage]: Error in Rename. Wrong syntax?", e);
                    }

                    if (hasObjectsDrobBox)
                        using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                        {
                            return dropbox.Rename(DropBoxGetRealPath(keywords));
                        }
                }

                return true;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in Rename", e);
                return false;
            }
        }

        public static bool RemoveUnusedTags()
        {
            try
            {
                if (!local.RemoveUnusedTags())
                    return false;

                if (useDropBox)
                {
                    using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                    {
                        return dropbox.RemoveUnusedTags();
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in RemoveUnusedTags", e);
                return false;
            }
        }

        public static bool Delete(string[] keywords)
        {
            try
            {
                if (!local.Delete(keywords))
                    return false;

                if (useDropBox)
                {
                    // Если хотя бы один объект находится в DropBox, то выполняем
                    // операцию в базе данных DropBox

                    // Синтаксис:
                    // DELETE [OBJECT | CATEGORY] object1 object 2

                    bool hasObjectsDrobBox = false;

                    try
                    {
                        for (int i = 2; i < keywords.Length; i ++)
                            if (Setup.InDropBox(keywords[i]))
                            {
                                hasObjectsDrobBox = true;
                                break;
                            }
                    }
                    catch (Exception e)
                    {
                        Setup.Log("[storage]: Error in Delete. Wrong syntax?", e);
                    }

                    if (hasObjectsDrobBox)
                        using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                        {
                            return dropbox.Delete(DropBoxGetRealPath(keywords));
                        }
                }

                return true;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in Delete", e);
                return false;
            }
        }

        public static bool ClearDatabase()
        {
            try
            {
                if (!local.ClearDatabase())
                    return false;

                if (useDropBox)
                {
                    using (DriverStorage dropbox = new DriverStorage(Setup.DropBoxDatabasePathFile))
                    {
                        return dropbox.ClearDatabase();
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in ClearDatabase", e);
                return false;
            }
        }

        public static void Disconnect()
        {
            try
            {
                // Отсоединение происходит только от локальной базы данных, т.к.
                // постоянное соединение с базой данных DropBox отсутствует

                local.Disconnect();
            }
            catch (Exception e)
            {
                Setup.Log("[storage]: Error in Disconnect", e);
            }
        }
    }
}
